package jplat.core.trans;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import ebcs.scan.bean.sys.SysUserInfor;
import jplat.base.app.parser.IAppPackParser;
import jplat.base.app.parser.impl.JAppPackJsonParser;
import jplat.base.constant.KPlatResponseCode;
import jplat.base.user.IUserInfoGetters;
import jplat.base.user.impl.JUserInfoGettersSession;
import jplat.core.session.JSession;
import jplat.error.exception.JSystemException;
import jplat.tools.config.JAppConfig;
import jplat.tools.config.JLogConfig;
import jplat.tools.string.JLogUtils;
import jplat.tools.string.StringUtil;
import jplat.tools.string.XLog;

/**
 * App请求服务类的应用上下文工厂实现，
 * 上下文是指一次App请求的上下文,携带了本次请求的全部信息.
 * @author zhangcq
 * @date Dec 7, 2016
 * @comment
 */

public class JAppContextFactory
{
	private int sessType = 0;		//会话类型.

	//查询用户信息的实现类.
	private IUserInfoGetters userInfoCmp = new JUserInfoGettersSession();

	//解析app传送过来的数据.
	private IAppPackParser appParser = new JAppPackJsonParser();

	private Logger logger = Logger.getLogger(JAppContextFactory.class);

	public JAppContextFactory()
	{
		sessType = JAppConfig.SESSION_TYPE;
	}

	public JAppContextFactory( int sess_type )
	{
		sessType = sess_type;
	}

	/**
	 * 获取带客户信息的上下文.
	 * 使用WithUser表示会话，其实不一定是用户，只是更直观方法的作用.
	 * @author zhangcq
	 * @date Dec 7, 2016
	 * @comment 
	 * @param request
	 * @return
	 * @throws JSystemException
	 */
	public JAppContext buildAppContextWithUser( HttpServletRequest request, HttpServletResponse response  ) throws JSystemException
	{
		return buildAppContext(request,response,true);
	}

	/**
	 * 获取不带客户信息的上下文.
	 * @author zhangcq
	 * @date Dec 7, 2016
	 * @comment 
	 * @param request
	 * @return
	 * @throws JSystemException
	 */
	public JAppContext buildAppContextNoUser( HttpServletRequest request, HttpServletResponse response ) throws JSystemException
	{
		return buildAppContext(request,response,false);
	}

	/**
	 * 建立请求应用上下文.
	 * @author zhangcq
	 * @date Dec 7, 2016
	 * @comment 
	 * @param request
	 * @param response
	 * @param needUser
	 * @param diyInput	自己接管输入.
	 * @return
	 * @throws JSystemException
	 */
	private JAppContext buildAppContext( HttpServletRequest request,
			HttpServletResponse response , boolean needUser ) throws JSystemException
	{
		JAppContext context = new JAppContext();
		context.config(request, response);
		context.setSessType(sessType);

		//解析请求头.
		if( !parseHttpHeaderInfo( context ) )
		{
			logger.error(XLog.CONN_MARK+"解析请求头出错.");
			throw new JSystemException(KPlatResponseCode.CD_INPUT_ERROR,KPlatResponseCode.MSG_INPUT_ERROR+",t=h");
		}

		//解析App传送过来的数据.
		//但是不解析二进制数据.
		if ( !context.getReqInfo().isBin() )
		{
			appParser.doAppParse(context);
		}

		//根据令牌获取客户信息.
		if ( needUser )
		{
			//			JAppConnectInfo connInfo = context.getReqInfo();
			SysUserInfor user = userInfoCmp.getUserInfo(context);
			context.setUserInfo(user);
		}

		//设置解析器.
		context.setAppParser(appParser);

		return context;
	}

	/**
	 * 此上下文建立只管理了会话.
	 * @author zhangcq
	 * @date Jan 18, 2017
	 * @comment 
	 * @param request
	 * @param response
	 * @param ttkn2
	 * @return
	 * @throws JSystemException
	 */
	public JAppContext buildPrivateContext( HttpServletRequest request,
			HttpServletResponse response , String ttkn2 ) throws JSystemException
	{
		JAppContext context = new JAppContext();
		context.config(request, response);
		context.setSessType(sessType);
		
		//设置token.
		JAppConnectInfo reqInfo = new JAppConnectInfo();
		reqInfo.setStoken(ttkn2);
		context.setReqInfo(reqInfo);
		
		//获取会话信息.
		if( context.getSessType() == JSession.SESS_HTTP ) //判断会话类型.
		{
			//JSession session = context.getSession();
		}

		//			JAppConnectInfo connInfo = context.getReqInfo();
		SysUserInfor user = userInfoCmp.getUserInfo(context);
		context.setUserInfo(user);
		
		//设置解析器.
		context.setAppParser(appParser);
		return context;
	}

	/**
	 * 解析通用报文头信息.
	 * @author zhangcq
	 * @date Nov 30, 2016
	 * @comment 
	 * @param ctx
	 * @return
	 */
	private boolean parseHttpHeaderInfo( JAppContext ctx )
	{
		if( JLogConfig.canPrintNetInfo() )
		{
			logger.info("__HTTP_HEADERS:"+JLogUtils.getHeadValues(ctx.getRequest()));
		}

		//解析请求头.
		Map<String,String> reqInfoMap = getReqInfoMap( ctx );
		
		JAppConnectInfo reqInfoModel = new JAppConnectInfo();
		reqInfoModel.setReqChl((String)reqInfoMap.get(JAppConnectInfo.K_CHL_TYPE));
		reqInfoModel.setCliVersion((String)reqInfoMap.get(JAppConnectInfo.K_CLI_VERSION));
		reqInfoModel.setEncType((String)reqInfoMap.get(JAppConnectInfo.K_ENC_TYPE));
		reqInfoModel.setStoken((String)reqInfoMap.get(JAppConnectInfo.H_HTOKEN_NAME));	//会话令牌(非身份认证令牌)
		reqInfoModel.setIpAddr(StringUtil.getIpAddr(ctx.getRequest()));					//获取客户端出口的IP地址.

		String contentType = ctx.getRequest().getContentType();
		reqInfoModel.setContentType(contentType);
		if ( contentType != null 
				&& JAppConnectInfo.V_CONTENT_TYPE_OCET.equals(contentType.toLowerCase()) )
		{
			reqInfoModel.setBin(true); //二进制数据.
		}

		//设置返回请求信息.
		ctx.setReqInfo(reqInfoModel);

		return true;
	}

	/**
	 * 获取会话令牌信息.
	 * 优先使用Cookie里面的令牌;
	 * 但是没有HTTP-Only的保护,令牌容易被盗.
	 * 目前暂时没有客户可以看大另外客户留言的场景，所以相对安全.
	 * @author zhangcq
	 * @date Nov 30, 2016
	 * @comment 
	 * @param ctx
	 * @return
	 */
	private Map<String,String> getReqInfoMap( JAppContext ctx )
	{
		Map<String,String> reqInfoMap = new HashMap<String,String>();
		
		//从头中获取.
		String info = ctx.getRequest().getHeader(JAppConnectInfo.H_INFO_KEY);
		if ( StringUtil.isNotEmpty(info) )
		{
			String[] parts = info.split(";");
			for ( int i = 0; i < parts.length; ++i )
			{
				String[] pairs = parts[i].split("=");
				if ( pairs.length == 2 )
				{
					reqInfoMap.put(pairs[0].trim(), pairs[1].trim());
				}
				else
				{
					reqInfoMap.put(pairs[0].trim(), "SET");
				}
			}
		}
		//从Cookie中获取.
		Cookie[] cookies = ctx.getRequest().getCookies();
		if ( cookies != null )
		{
			for ( int i = 0; i < cookies.length; ++i )
			{
				if( JLogConfig.canPrintNetInfo() )
				{
					logger.info("Cookie::"+cookies[i].getName()+"="+cookies[i].getValue());
				}
				
				reqInfoMap.put(cookies[i].getName(), cookies[i].getValue());
			}
		}
		
		//所以,如果值重复,最后是以Cookie中的为准.
		
		return reqInfoMap;
	}

	//设置会话类型.
	public int getSessType() {
		return sessType;
	}

	//@@设置会话类型.
	public JAppContextFactory setSessType(int sessType) {
		this.sessType = sessType;

		return this;
	}

}
